iT邦幫忙

2021 iThome 鐵人賽

DAY 15
1

前情提要

艾草:「好了,總算選取到樹上的紅色果實了,那我來教你一些簡單的火屬性魔法事件。」

「咦,我的魔力總量夠了嗎?總算能開始了嗎!」

艾草:「我琢磨著你的成長,已經夠了,你可以學初階魔法了。來想像你的手指有一股魔力,好像你前方有隱形的按鈕,去觸碰它。」

「喔喔喔喔~~有感覺了呃啊啊啊啊啊。」

(手指尖端冒出了小小的火燄,與此同時手指也被灼傷了。)

艾草:「喔,成功了耶!幫你澆水熄滅唷。」

「嗚嗚嗚,好痛。還會燙傷人!我學這個幹嘛,還不如用打火機!」

艾草:「是你現在魔力總量不足啦,對魔法事件的掌握度不夠,我們先來研讀一下事件的機制吧!」

(艾草心裡 os 當初就叫你不要選火屬性吧(๑•́ ₃ •̀๑))


Event

透過 Event 我們可以讓網站與使用者有更多的互動,但 Event 到底是什麼呢?

拿常見的彈跳式視窗來舉例好了,我們在某些網站購物時,當點擊到確認付款後,可能會跳出付款成功,而這個功能是如何實現的呢?

沒錯,就是透過 Event ,Event 包含各式各樣的類型,例如剛剛提到的滑鼠點擊、又或是敲打鍵盤

等,通常都是使用者執行到某些特殊事件時 ,會去執行對應的程式碼,而今天會介紹一些常見的 Event 類型。

事件傳遞流程 Event Flow

為什麼要談到事件傳遞流程 Event Flow 呢?

今天當你開心的點擊了某顆按鈕時,你以為你只點擊到它嗎?這樣想就太天真了,其實你不僅僅點擊到它,如果延伸來看,你還點擊到它的父層、更甚者你點擊到的是整個 document 。

為什麼會這樣說呢?因為按鈕可能包覆在 div 內,而 div 可能包覆在 body 內,這樣層層延伸下去,其實你點擊到的是整個網頁,也因此網頁會有一定的事件傳遞流程。

而事件傳遞流程分為兩種:

  • Event Capture 事件捕獲
  • Event Bubble 事件冒泡

而 「Capture 捕獲」、「Bubble 冒泡」分別指的是你的事件就怎麼觸發的呢?讓我們先來看一張 W3C 的圖,會更清楚。

https://ithelp.ithome.com.tw/upload/images/20210929/20139066qZ0fhl1dkk.png

圖片來源:W3C

Capture Phase 捕獲

捕獲階段指得是由上而下,從最 window 到目標階段 Target Phase 的過程。

Target Phase 目標階段

目標階段指得是找到目標 Target 時。

Bubble Phase 冒泡

冒泡階段指得是由下而上,從目標階段 Target Phase 到 window 的過程。

那這其中會帶來怎樣的差異呢?讓我們透過 addEventListener 監聽來了解吧。


addEventListener()

可以透過 addEventListener() 來監聽我們目前觸發的事件類型後,去執行對應的功能。

addEventListener() 有三個參數:

  1. type : 事件類型
  2. listener :對應的事件處理函式
  3. useCapture:Boolean 值,決定事件是以 true「Capture 捕獲」或 false「Bubble 冒泡」機制執行,預設為 false。

事件類型要透過包覆單引號、雙引號的方式去呈現,例如:'click'。

事件處理函式代表如果使用者點擊了我所監聽的按鈕時,應該會有什麼樣的功能,通常程式碼會包覆在函式內。

而 useCapture 設定 true 、 false 的差異性,讓我們透過以下程式碼來了解:

Capture 捕獲

HTML:

<table>
    <thead>
    </thead>
    <tbody>
        <tr>
            <td>123</td>
            <td>321</td>
        </tr>
    </tbody>
</table>

JavaScript:

const table = document.querySelector("table");
const tbody = document.querySelector("tbody");
const tr = document.querySelector("tr");
const td = document.querySelector("td");

table.addEventListener("click", function(e) {
  console.log('table click')
},true);
tbody.addEventListener("click", function(e) {
  console.log('tbody click')
},true);
tr.addEventListener("click", function(e) {
  console.log('tr click')
},true);
td.addEventListener("click", function(e) {
  console.log('td click')
},true);

執行後的結果:
https://ithelp.ithome.com.tw/upload/images/20210929/20139066qGp9FpuyIT.png

Bubble 冒泡

false 為預設情況。

HTML:

<table>
    <thead>
    </thead>
    <tbody>
        <tr>
            <td>123</td>
            <td>321</td>
        </tr>
    </tbody>
</table>

JavaScript:

const table = document.querySelector("table");
const tbody = document.querySelector("tbody");
const tr = document.querySelector("tr");
const td = document.querySelector("td");

table.addEventListener("click", function(e) {
  console.log('table click')
});
tbody.addEventListener("click", function(e) {
  console.log('tbody click')
});
tr.addEventListener("click", function(e) {
  console.log('tr click')
});
td.addEventListener("click", function(e) {
  console.log('td click')
});

執行後的結果:
https://ithelp.ithome.com.tw/upload/images/20210929/20139066pcwaAixlwL.png

透過此方式可以看到 Capture 捕獲、Bubble 冒泡的差異, Capture 捕獲是從父層一層一層下來時就觸發動作,Bubble 冒泡則是從子層一層一層上去時觸發動作。

接下來讓我們實作看看註冊監聽點擊按鈕事件吧!

HTML:

<button type = "button">按鈕</button>

JavaScript:

//透過 querySelector 選取按鈕
const button = document.querySelector("button");
//為按鈕註冊監聽函式      //監聽點擊 //e 代表 event 可自定義參數
button.addEventListener("click", function(e) {
  console.log('button click')
});

執行後的結果:

https://ithelp.ithome.com.tw/upload/images/20210929/20139066uifEBns6Ky.png

我們透過上方程式碼 console.log 印出 event 看看長怎樣吧!

可以發現是一大包的物件,而將它展開後有很多的屬性:
https://ithelp.ithome.com.tw/upload/images/20210929/201390669zMfBxHpI7.png
https://ithelp.ithome.com.tw/upload/images/20210929/20139066rmmFxmaH78.png

接下來讓我們針對常用的屬性 e.target 做介紹吧!


e.target

e.target 表示觸發事件的該元素本身,什麼意思呢?

一樣透過剛剛的程式碼 console.log 出來就知道了!

JavaScript:

//透過 querySelector 選取按鈕
const button = document.querySelector("button");
//為按鈕註冊監聽函式      //監聽點擊 //e 代表 event 可自定義參數
button.addEventListener("click", function(e) {
  console.log(e.target)
});

印出結果:
https://ithelp.ithome.com.tw/upload/images/20210929/20139066hJBgJWbjWt.png

用這樣可能看不太出來它好用在哪邊,但 e.target 常常拿來取值,或是拿來做流程判斷,以下舉例:

例如當大範圍監聽時,搭配 e.target 底下的屬性 nodeName 判斷點擊到的是不是按鈕:

HTML:

<ul class="list">
	<button type="button">送出</button>
</ul>

JavaScript:

const list = document.querySelector(".list");
list.addEventListener("click", function (e) {
    if(e.target.nodeName === "BUTTON"){
        console.log("我點擊到的是按鈕!")
    }
});

透過 e.target 搭配 nodeName 的方式可以確認當點擊到的是按鈕時,才開始撰寫對應的效果!


event.preventDefault()

event.preventDefault() 可以拿來阻擋預設行為,例如 a 連結點擊會轉址的行為,又或者是表單的

submit 效果,以下來示範該如何實作!

HTML:

<a href="https://www.google.com.tw/">連結</a>

JavaScript:

const link = document.querySelector("a");

link.addEventListener("click", function (e) {
		//阻擋 a 連結轉址效果
    e.preventDefault();
    console.log("我被點了")
});

像這樣直接在 link 的註冊監聽函式內放入 event.preventDefault() a 連結就不會有轉址的效果囉!

另外如果使用 button 時需要特別注意,如果沒有指定 button 的 type 屬性,預設就會是 submit ,要特別留意。


小練習

請問以下敘述何者錯誤?

A addEventListener() useCapture 參數設定 true 時,會透過 Capture 捕獲機制執行
B Capture 捕獲機制會由父層到子層觸發,且 addEventListener() useCapture 參數預設就是 true
C e.target 表示觸發事件的元素本身
D event.preventDefault() 可以阻擋預設行為,例如表單的 submit 效果

解答:選項 B 錯誤,因為useCapture 參數預設為 false ,預設是透過 Bubble 冒泡執行。


參考文獻

JavaScript 必修篇 - 前端修練全攻略(六角學院)
https://ithelp.ithome.com.tw/articles/10192015
https://medium.com/itsems-frontend/javascript-event-bubbling-capturing-794cd2d01e61


上一篇
入門魔法 - 針對 DOM 節點的簡單操作
下一篇
入門魔法 - AJAX
系列文
JavaScript 魔法入門 - 從入門到中階觀念 共 30 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言